package core.swarm;

import utility.*;
import core.*;
import core.genetic.*;
import core.genetic.selection.*;

public class SwarmAdvSelection  implements Selection {

	final int dimensions, popSize;
	double vmax;
	Population pid;
	double[] bestSoFar;
	double[][] v;
	double inw; // inertia weight
	int iteration = 0;
	final int close;
	final int mutCount;
	final int learnCount;
	private final int learningCasesCount;
	private final double learningKoef;
	
	public SwarmAdvSelection(int close, int mutCount, int learnCount, int learningCasesCount, double learningKoef) {
		dimensions = Mode.mode.geneSize;
		popSize = Mode.mode.popSize;
		vmax = 2;
		inw = 0.9;
		this.close = close;
		this.mutCount = mutCount;
		this.learnCount = learnCount;
		this.learningCasesCount =learningCasesCount;
		this.learningKoef = learningKoef;
		
		bestSoFar = null;
		pid = null;
		v = new double[Mode.mode.popSize][dimensions];
	}

	private boolean firstIter = true;

	@Override
	public void select(Population pop, double[] fitness) {
		inw = Math.max(0.4, 0.9 * (iteration / Mode.mode.nbGenerations));

		if (firstIter) {
			firstIter = false;
			pid = new Population(Mode.mode.geneSize);
			bestSoFar = fitness.clone();
			for (int i = 0; i < popSize; i++) 
				pid.get(i).set(pop.get(i));							
		}

		for (int i = 0; i < popSize; i++) {
			if (fitness[i] > bestSoFar[i]) {
				bestSoFar[i] = fitness[i]; 			
				pid.get(i).set(pop.get(i));
			}
			
			int g = i;
			for(int k = -close;k<=close;k++) {
				int j = (i+k+fitness.length)%fitness.length;
				if (bestSoFar[j]>bestSoFar[g])
					g = j;
			}
			
//			if (fitness[(i + 2) % fitness.length] > bestSoFar[i])
//				g = (i + 2) % fitness.length;
//			else if (fitness[(i + 1) % fitness.length] > bestSoFar[i])
//				g = (i + 1) % fitness.length;
//			else if (fitness[(i - 1 + fitness.length) % fitness.length] > bestSoFar[i])
//				g = (i - 1 + fitness.length) % fitness.length;
//			else if (fitness[(i - 2 + fitness.length) % fitness.length] > bestSoFar[i])
//				g = (i - 2 + fitness.length) % fitness.length;

			for (int d = 0; d < dimensions; d++) {
				double fi1 = Utils.random.nextDouble() * 2;
				double fi2 = Utils.random.nextDouble() * 2;
				v[i][d] = inw * v[i][d] + fi1
						* (pid.get(i).getGene(d) - pop.get(i).getGene(d)) + fi2
						* (pid.get(g).getGene(d) - pop.get(i).getGene(d));
				v[i][d] = Math.min(Math.max(v[i][d], -vmax), vmax);
				pop.get(i).setGene(d, pop.get(i).getGene(d) + v[i][d]);
			}
		}

		if (mutCount + learnCount>0) {
			double[] pom = new double[fitness.length];
			for(int i = 0;i<fitness.length;i++)
				pom[i] = -fitness[i];
		
			double[] ver = Utils.calcCumulativeProb(pom,pom.length);
			double[] good = Utils.calcCumulativeProb(fitness,fitness.length);
		
			for(int i = 0;i<mutCount;i++)
			{
				int z = Utils.randomW(ver);
				Mode.mode.mutation.mutate(pop.get(z));
			}

			for(int i = 0;i<learnCount;i++) 
			{
				int z = Utils.randomW(ver);
				int cale = Utils.randomW(good);
				int keva = Utils.randomW(good);
				for(int j = 0;cale==keva && j<10;j++)
					keva = Utils.randomW(good);
				pop.get(z).set(pop.get(cale));
				Mode.mode.brain.learn(pop.get(z), pop.get(keva), learningKoef, learningCasesCount);			
			}			
		}		
		iteration++;
	}

}
